/*
 * Created by JFormDesigner on Fri Sep 20 21:56:49 CST 2024
 */

package com.fangda.netool.ui;

import javax.swing.border.*;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import com.fangda.netool.dto.CbDto;
import com.fangda.netool.util.Constants;
import com.fangda.netool.util.MessageUtil;
import com.fangda.network.*;
import com.fangda.network.tcp.client.TcpClientProvider;
import com.fangda.network.tcp.server.TcpServerNetwork;
import com.fangda.network.tcp.server.TcpServerProvider;
import com.fangda.network.udp.UdpProvider;
import com.fangda.network.websocket.client.WebsocketClientNetwork;
import com.fangda.network.websocket.client.WebsocketClientProvider;
import com.fangda.network.websocket.server.WebsocketServerNetwork;
import com.fangda.network.websocket.server.WebsocketServerProvider;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.net.NetSocket;

import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.swing.*;

/**
 * 主界面
 * @author 赵方方
 */
public class MainPanel extends JFrame {
    public MainPanel() {
        initComponents();
        initData();
    }

    private Network network;
    private void initData() {
        //
        ipText.setVisible(false);
        ipCb.setVisible(true);
        //初始化协议选择
        initProtocolCb();
        //初始化打开按钮
        initOpenBtn();
        //初始化发送按钮
        initSendBtn();
        //2个清除按钮
        sendClearBtn.addActionListener(e -> sendTa.setText(""));
        recieveClearBtn.addActionListener(e -> recieveTa.setText(""));
    }

    /**
     * 初始化协议选择 组件框
     */
    private void initProtocolCb() {
        protocolCb.addItem(new CbDto(Constants.WS_SERVER, "WS_SERVER"));
        protocolCb.addItem(new CbDto(Constants.WS_CLIENT, "WS_CLIENT"));
        protocolCb.addItem(new CbDto(Constants.UDP_SERVER, "UDP_SERVER"));
        protocolCb.addItem(new CbDto(Constants.UDP_CLIENT, "UDP_CLIENT"));
        protocolCb.addItem(new CbDto(Constants.TCP_SERVER, "TCP_SERVER"));
        protocolCb.addItem(new CbDto(Constants.TCP_CLIENT, "TCP_CLIENT"));
        protocolCb.addActionListener(e -> {
            CbDto cdDto = (CbDto)protocolCb.getSelectedItem();
            changeState(cdDto);
        });
        //IP
        Set<String> ips = NetUtil.localIpv4s();
        for (String ip : ips) {
            ipCb.addItem(ip);
        }
    }

    /**
     * 发送按钮初始化
     */
    private void initSendBtn() {

        sendBtn.addActionListener(e -> {
            String txt = sendTa.getText();
            if(StrUtil.isBlank(txt)) {
                return;
            }
            NetworkMessage msg = new NetworkMessage();
            msg.setPayload(txt.getBytes(StandardCharsets.UTF_8));
            CbDto cdDto = (CbDto)protocolCb.getSelectedItem();
            String value = cdDto.getValue();
            String selectObj = (String)clientsCb.getSelectedItem();

            if(StrUtil.equals(value, "WS_SERVER") && StrUtil.isNotBlank(selectObj) && !StrUtil.equals(selectObj, "All")) {
                WebsocketServerNetwork ws = (WebsocketServerNetwork)network;
                List<ServerWebSocket> sessions = ws.getSessions();
                for (ServerWebSocket session : sessions) {
                    if(StrUtil.equals(session.remoteAddress().toString(), selectObj)) {
                        ws.send(msg, (result, removeAddress) -> {
                            if (result.succeeded()) {
                                String m = new String(txt.getBytes(StandardCharsets.UTF_8));
                                recieveTa.append(MessageUtil.getMessage(m, null, removeAddress.toString()));
                            }
                        }, List.of(session));
                    }
                }
            } else if(StrUtil.equals(value, "TCP_SERVER") && StrUtil.isNotBlank(selectObj) && !StrUtil.equals(selectObj, "All")) {
                TcpServerNetwork tcpServer = (TcpServerNetwork)network;
                List<NetSocket> sessions = tcpServer.getSessions();
                for (NetSocket session : sessions) {
                    if(StrUtil.equals(session.remoteAddress().toString(), selectObj)) {
                        tcpServer.send(msg, (result, removeAddress) -> {
                            if (result.succeeded()) {
                                String m = new String(txt.getBytes(StandardCharsets.UTF_8));
                                recieveTa.append(MessageUtil.getMessage(m, null, removeAddress.toString()));
                            }
                        }, List.of(session));
                    }
                }
            } else {
                if(StrUtil.equals(value, "UDP_CLIENT")) {
                    String ip = Objects.requireNonNull(ipText.getText());
                    Integer port = Integer.parseInt(portText.getText());
                    network = NetworkManager.getInstance().createNetwork(ip, port, pathText.getText(), new UdpProvider(), getNetworkCallback(),  getNetWorkParser());
                }
                network.send(msg, (result, removeAddress) -> {
                    if (result.succeeded()) {
                        String m = new String(txt.getBytes(StandardCharsets.UTF_8));
                        recieveTa.append(MessageUtil.getMessage(m, null, removeAddress.toString()));
                    }
                });
            }
        });
    }

    private void initOpenBtn() {
        openBtn.setText("打开");
        openBtn.addActionListener(e -> {
            CbDto cdDto = (CbDto)protocolCb.getSelectedItem();
            String value = cdDto.getValue();
            if(StrUtil.equals(value, "WS_SERVER") || StrUtil.equals(value, "UDP_SERVER") || StrUtil.equals(value, "TCP_SERVER")) {
                if(StrUtil.equals("打开", openBtn.getText())) {
                    open();
                } else if(StrUtil.equals("关闭", openBtn.getText())) {
                    network.shutdown();
                }
            } else if(StrUtil.equals(value, "WS_CLIENT") || StrUtil.equals(value, "UDP_CLIENT") || StrUtil.equals(value, "TCP_CLIENT")) {
                if(StrUtil.equals("连接", openBtn.getText())) {
                    connect();
                } else if(StrUtil.equals("断开", openBtn.getText())) {
                    if(StrUtil.equals(value, "WS_CLIENT") || StrUtil.equals(value, "TCP_CLIENT")) {
                        network.shutdown();
                    }
                }
            }
        });
    }

    /**
     * 协议选择变化引起的界面变化
     * @param cdDto
     */
    private void changeState(CbDto cdDto) {
        if(StrUtil.equals(cdDto.getValue(), "WS_SERVER") || StrUtil.equals(cdDto.getValue(), "UDP_SERVER") || StrUtil.equals(cdDto.getValue(), "TCP_SERVER")) {
            ipText.setVisible(false);
            ipCb.setVisible(true);
            openBtn.setEnabled(true);
            openBtn.setText("打开");
            ipCb.setSelectedIndex(0);
            portText.setText("");
            pathText.setEnabled(true);
            if(StrUtil.equals(cdDto.getValue(), "UDP_SERVER") || StrUtil.equals(cdDto.getValue(), "TCP_SERVER")) {
                pathText.setEnabled(false);
            }
        } else {
            ipCb.setVisible(false);
            ipText.setVisible(true);
            openBtn.setEnabled(true);
            openBtn.setText("连接");
            portText.setText("");
            pathText.setEnabled(true);
            if(StrUtil.equals(cdDto.getValue(), "UDP_CLIENT")) {
                pathText.setEnabled(false);
                openBtn.setEnabled(false);
            }
        }
    }

    /**
     * 启动server
     */
    private void open() {
        if(!check()) {
            return;
        }
        openBtn.setEnabled(false);
        openBtn.setText("打开中");

        CbDto cdDto = (CbDto)protocolCb.getSelectedItem();
        String value = cdDto.getValue();
        String ip;
        if(ipText.isVisible()) {
            ip = Objects.requireNonNull(ipText.getText());
        } else {
            ip = (String)ipCb.getSelectedItem();
        }

        Integer port = Integer.parseInt(portText.getText());

        NetworkProvider provider = null;

        if(StrUtil.equals(value, "WS_SERVER")) {
            provider = new WebsocketServerProvider();
        } else if(StrUtil.equals(value, "UDP_SERVER")) {
            provider = new UdpProvider();
        } else if(StrUtil.equals(value, "TCP_SERVER")) {
            provider = new TcpServerProvider();
        }

        network = NetworkManager.getInstance().createNetwork(ip, port, pathText.getText(), provider, getNetworkCallback(),  getNetWorkParser());
        try {
            network.getClass().getMethod("startup").invoke(network);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }



    private void connect() {
        if(!check()) {
            return;
        }
        openBtn.setEnabled(false);
        openBtn.setText("连接中");

        CbDto cdDto = (CbDto)protocolCb.getSelectedItem();
        String value = cdDto.getValue();
        NetworkProvider provider = null;

        if(StrUtil.equals(value, "WS_CLIENT")) {
            provider = new WebsocketClientProvider();
        } else if(StrUtil.equals(value, "UDP_CLIENT")) {
            provider = new UdpProvider();
        } else if(StrUtil.equals(value, "TCP_CLIENT")) {
            provider = new TcpClientProvider();
        }
        String ip = Objects.requireNonNull(ipText.getText());
        Integer port = Integer.parseInt(portText.getText());
        network = NetworkManager.getInstance().createNetwork(ip, port, pathText.getText(), provider, getNetworkCallback(),  getNetWorkParser());
        if(!StrUtil.equals(value, "UDP_CLIENT")) {
            try {
                network.getClass().getMethod("connect").invoke(network);
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private NetworkCallback getNetworkCallback() {
        return new NetworkCallback() {
            @Override
            public void connectionChange(boolean connectStatus, Throwable exception) {
                setStatus();
                if (exception != null) {
                    exception.printStackTrace();
                    openBtn.setEnabled(true);
                    openBtn.setText("打开");
                } else {
                    if(connectStatus) {
                        openBtn.setEnabled(true);
                        openBtn.setText("关闭");
                    } else {
                        openBtn.setEnabled(true);
                        openBtn.setText("打开");
                    }
                }
            }

            @Override
            public void exceptionHandler(Throwable throwable) {
                openBtn.setEnabled(true);
                openBtn.setText("打开");
                statusLabel.setText("打开失败：" + throwable.getMessage());
            }
        };
    }

    private NetWorkParser<Object> getNetWorkParser() {
        return message -> {
            String txt = new String(message.getPayload());
            String m = new String(txt.getBytes(StandardCharsets.UTF_8));
            recieveTa.append(MessageUtil.getMessage(m, message.getRemoteAddress(), ""));
            return null;
        };
    }

    private void setStatus() {
        CbDto cdDto = (CbDto)protocolCb.getSelectedItem();
        String value = cdDto.getValue();
        String txt = "";
        if(StrUtil.equals(value, "WS_SERVER")) {
            if(network.isAlive()) {
                txt = String.format(Constants.SUC_SERVER, network.getNetworkType().toString(), network.getUrl());
                List<ServerWebSocket> list = ((WebsocketServerNetwork)network).getSessions();
                statusLabel.setText(txt + "，目前有 " + (list == null? 0 : list.size()) + "个客户端连接");
                clientsCb.removeAllItems();
                clientsCb.addItem("All");
                if(list != null) {
                    for(ServerWebSocket webSocket : list) {
                        clientsCb.addItem(webSocket.remoteAddress().toString());
                    }
                }
            } else {
                txt = String.format(Constants.ERR_SERVER, network.getNetworkType().toString(), network.getUrl());
                statusLabel.setText(txt);
                clientsCb.removeAllItems();
            }

        } else if(StrUtil.equals(value, "UDP_SERVER")) {
            if(network.isAlive()) {
                txt = String.format(Constants.SUC_SERVER, network.getNetworkType().toString(), network.getUrl());
                clientsCb.removeAllItems();
                clientsCb.addItem(network.getUrl());
            } else {
                txt = String.format(Constants.ERR_CLIENT, network.getNetworkType().toString(), network.getUrl());
            }
            statusLabel.setText(txt);
        } else if(StrUtil.equals(value, "TCP_SERVER")) {
            if(network.isAlive()) {
                txt = String.format(Constants.SUC_CLIENT, network.getNetworkType().toString(), network.getUrl());
                List<NetSocket> list = ((TcpServerNetwork)network).getSessions();
                statusLabel.setText(txt + "，目前有 " + (list == null? 0 : list.size()) + "个客户端连接");
                //TODO
                clientsCb.removeAllItems();
                clientsCb.addItem("All");
                if(list != null) {
                    for(NetSocket webSocket : list) {
                        clientsCb.addItem(webSocket.remoteAddress().toString());
                    }
                }
            } else {
                statusLabel.setText("无服务启动");
                clientsCb.removeAllItems();
            }
        } else if(StrUtil.equals(value, "TCP_CLIENT") || StrUtil.equals(value, "WS_CLIENT") || StrUtil.equals(value, "UDP_CLIENT")) {
            if(network.isAlive()) {
                txt = String.format(Constants.SUC_CLIENT, network.getNetworkType().toString(), network.getUrl());
            } else {
                txt = String.format(Constants.ERR_CLIENT, network.getNetworkType().toString(), network.getUrl());
            }
            clientsCb.removeAllItems();
            clientsCb.addItem(network.getUrl());
            statusLabel.setText(txt);
        } else {
            txt = "未知类型";
            statusLabel.setText(txt);
        }
    }

    private boolean check() {
        String ip;
        if(ipText.isVisible()) {
            ip = Objects.requireNonNull(ipText.getText());
        } else {
            ip = (String)ipCb.getSelectedItem();
        }
        if(StrUtil.isBlank(ip)) {
            JOptionPane.showMessageDialog(this, "IP地址不能为空");
            return false;
        }
        String port = Objects.requireNonNull(portText.getText());
        if(StrUtil.isBlank(port)) {
            JOptionPane.showMessageDialog(this, "端口不能为空");
            return false;
        }
        return true;
    }

    private void thisWindowClosed(WindowEvent e) {
        // TODO add your code here
    }

    private void initComponents() {
        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents  @formatter:off
        // Generated using JFormDesigner Open Source Project license - 淘宝星河飘雪
        tabbedPane1 = new JTabbedPane();
        panel1 = new JPanel();
        protocolLabel = new JLabel();
        protocolCb = new JComboBox();
        ipLabel = new JLabel();
        ipText = new JTextField();
        portLabel = new JLabel();
        portText = new JTextField();
        openBtn = new JButton();
        ipCb = new JComboBox();
        pathText = new JTextField();
        pathLabel = new JLabel();
        tabbedPane2 = new JTabbedPane();
        scrollPane2 = new JScrollPane();
        recieveTa = new JTextArea();
        tabbedPane3 = new JTabbedPane();
        scrollPane1 = new JScrollPane();
        sendTa = new JTextArea();
        sendBtn = new JButton();
        statusLabel = new JLabel();
        sendClearBtn = new JButton();
        recieveClearBtn = new JButton();
        clientsCb = new JComboBox();

        //======== this ========
        setTitle("\u6d4b\u8bd5");
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                thisWindowClosed(e);
            }
        });
        var contentPane = getContentPane();
        contentPane.setLayout(null);

        //======== tabbedPane1 ========
        {

            //======== panel1 ========
            {
                panel1.setLayout(null);

                //---- protocolLabel ----
                protocolLabel.setText("\u534f\u8bae\u7c7b\u578b\uff1a");
                protocolLabel.setHorizontalAlignment(SwingConstants.RIGHT);
                panel1.add(protocolLabel);
                protocolLabel.setBounds(-2, 5, 80, 24);
                panel1.add(protocolCb);
                protocolCb.setBounds(73, 5, 132, 25);

                //---- ipLabel ----
                ipLabel.setText("\u672c\u5730\u4e3b\u673aIP\uff1a");
                ipLabel.setHorizontalAlignment(SwingConstants.RIGHT);
                panel1.add(ipLabel);
                ipLabel.setBounds(-2, 35, 80, 24);
                panel1.add(ipText);
                ipText.setBounds(73, 35, 132, 25);

                //---- portLabel ----
                portLabel.setText("\u7aef\u53e3\uff1a");
                portLabel.setHorizontalAlignment(SwingConstants.RIGHT);
                panel1.add(portLabel);
                portLabel.setBounds(-2, 65, 80, 24);
                panel1.add(portText);
                portText.setBounds(73, 65, 132, 25);

                //---- openBtn ----
                openBtn.setText("\u6253\u5f00");
                panel1.add(openBtn);
                openBtn.setBounds(75, 123, 130, 29);

                //---- ipCb ----
                ipCb.setVisible(false);
                panel1.add(ipCb);
                ipCb.setBounds(73, 35, 132, 25);
                panel1.add(pathText);
                pathText.setBounds(75, 95, 130, 25);

                //---- pathLabel ----
                pathLabel.setText("\u8def\u5f84\uff1a");
                pathLabel.setHorizontalAlignment(SwingConstants.RIGHT);
                panel1.add(pathLabel);
                pathLabel.setBounds(0, 95, 80, 24);

                {
                    // compute preferred size
                    Dimension preferredSize = new Dimension();
                    for(int i = 0; i < panel1.getComponentCount(); i++) {
                        Rectangle bounds = panel1.getComponent(i).getBounds();
                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
                    }
                    Insets insets = panel1.getInsets();
                    preferredSize.width += insets.right;
                    preferredSize.height += insets.bottom;
                    panel1.setMinimumSize(preferredSize);
                    panel1.setPreferredSize(preferredSize);
                }
            }
            tabbedPane1.addTab("\u7f51\u7edc\u914d\u7f6e", panel1);
        }
        contentPane.add(tabbedPane1);
        tabbedPane1.setBounds(5, 5, 215, 205);

        //======== tabbedPane2 ========
        {

            //======== scrollPane2 ========
            {
                scrollPane2.setViewportView(recieveTa);
            }
            tabbedPane2.addTab("\u63a5\u6536\u6570\u636e", scrollPane2);
        }
        contentPane.add(tabbedPane2);
        tabbedPane2.setBounds(225, 5, 705, 360);

        //======== tabbedPane3 ========
        {

            //======== scrollPane1 ========
            {
                scrollPane1.setViewportView(sendTa);
            }
            tabbedPane3.addTab("\u53d1\u9001\u8bbe\u7f6e", scrollPane1);
        }
        contentPane.add(tabbedPane3);
        tabbedPane3.setBounds(225, 365, 705, 175);

        //---- sendBtn ----
        sendBtn.setText("\u53d1\u9001");
        contentPane.add(sendBtn);
        sendBtn.setBounds(835, 542, 90, sendBtn.getPreferredSize().height);

        //---- statusLabel ----
        statusLabel.setBackground(Color.black);
        statusLabel.setForeground(Color.black);
        statusLabel.setBorder(LineBorder.createBlackLineBorder());
        contentPane.add(statusLabel);
        statusLabel.setBounds(-1, 600, 940, 30);

        //---- sendClearBtn ----
        sendClearBtn.setText("\u6e05\u7a7a\u53d1\u9001");
        contentPane.add(sendClearBtn);
        sendClearBtn.setBounds(new Rectangle(new Point(225, 542), sendClearBtn.getPreferredSize()));

        //---- recieveClearBtn ----
        recieveClearBtn.setText("\u6e05\u7a7a\u63a5\u6536");
        contentPane.add(recieveClearBtn);
        recieveClearBtn.setBounds(320, 542, 95, recieveClearBtn.getPreferredSize().height);
        contentPane.add(clientsCb);
        clientsCb.setBounds(705, 545, 120, 25);

        {
            // compute preferred size
            Dimension preferredSize = new Dimension();
            for(int i = 0; i < contentPane.getComponentCount(); i++) {
                Rectangle bounds = contentPane.getComponent(i).getBounds();
                preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
                preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
            }
            Insets insets = contentPane.getInsets();
            preferredSize.width += insets.right;
            preferredSize.height += insets.bottom;
            contentPane.setMinimumSize(preferredSize);
            contentPane.setPreferredSize(preferredSize);
        }
        pack();
        setLocationRelativeTo(getOwner());
        // JFormDesigner - End of component initialization  //GEN-END:initComponents  @formatter:on
    }

    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables  @formatter:off
    // Generated using JFormDesigner Open Source Project license - 淘宝星河飘雪
    private JTabbedPane tabbedPane1;
    private JPanel panel1;
    private JLabel protocolLabel;
    private JComboBox protocolCb;
    private JLabel ipLabel;
    private JTextField ipText;
    private JLabel portLabel;
    private JTextField portText;
    private JButton openBtn;
    private JComboBox ipCb;
    private JTextField pathText;
    private JLabel pathLabel;
    private JTabbedPane tabbedPane2;
    private JScrollPane scrollPane2;
    private JTextArea recieveTa;
    private JTabbedPane tabbedPane3;
    private JScrollPane scrollPane1;
    private JTextArea sendTa;
    private JButton sendBtn;
    private JLabel statusLabel;
    private JButton sendClearBtn;
    private JButton recieveClearBtn;
    private JComboBox clientsCb;
    // JFormDesigner - End of variables declaration  //GEN-END:variables  @formatter:on
}
